package satellite

import (
	"errors"
	"io"
	"io/ioutil"
)

type CryptoType = byte

const (
	NoneCrypto CryptoType = iota
)

type Compress interface {
	Writer(writer io.Writer) io.WriteCloser
	ReleaseWriter(writer io.Writer)
	Reader(reader io.Reader) io.Reader
	ReleaseReader(reader io.Reader)
}

type Protocol interface {
	Read(conn Conn) (*Packet, error)
	Write(code []byte, conn Conn, handler func(writer io.Writer) error) (int, error)
}

type DataPacket interface {
	Encode(writer io.Writer) error
	Decode(reader io.Reader) error
}

type Message interface {
	ReadBytes() []byte
	ReadString() string
	Reader() io.Reader
	ReadData(data DataPacket)
}

type Packet struct {
	Head        []byte
	Length      int
	readHandle  []func() error
	reader      io.Reader
	closeHandle []func()
	closed      bool
	err         error
}

func NewPacket() *Packet {
	packet := Packet{
		readHandle:  []func() error{},
		closeHandle: []func(){},
		closed:      false,
	}
	return &packet
}

func (p *Packet) onRead(fn func() error) {
	p.readHandle = append(p.readHandle, fn)
}

func (p *Packet) onClose(fn func()) {
	p.closeHandle = append(p.closeHandle, fn)
}

func (p *Packet) ReadBytes() []byte {
	p.Reader()
	if p.err != nil {
		return nil
	}
	b, err := ioutil.ReadAll(p.reader)
	if err != nil {
		p.err = err
		return nil
	}
	return b
}

func (p *Packet) ReadString() string {
	p.Reader()
	b, err := ioutil.ReadAll(p.reader)
	if err != nil {
		p.err = err
		return ""
	}
	return string(b)
}

func (p *Packet) ReadData(data DataPacket) {
	p.Reader()
	if p.reader == nil {
		p.err = errors.New("packet is miss")
		return
	}
	err := data.Decode(p.reader)
	if err != nil {
		p.err = err
	}
}

func (p *Packet) Reader() io.Reader {
	if p.reader == nil {
		for k := range p.readHandle {
			if err := p.readHandle[k](); err != nil {
				p.err = err
				return nil
			}
		}
	}
	return p.reader
}

func (p *Packet) Close() error {
	if p.closed {
		return p.err
	}
	if p.err != nil {
		p.reader = nil
	}

	p.closed = true
	for k := range p.closeHandle {
		p.closeHandle[k]()
	}
	return p.err
}

func (p *Packet) reset() {
	p.closed = false
	p.reader = nil
	p.Head = nil
	p.err = nil
	p.readHandle = p.readHandle[:0]
	p.closeHandle = p.closeHandle[:0]
}
